/* ** por compatibilidad se omiten tildes **
================================================================================
 TRABAJO PRACTICO 3 - System Programming - ORGANIZACION DE COMPUTADOR II - FCEN
================================================================================
  definicion de funciones del manejador de memoria
*/

#include "mmu.h"

unsigned int cr3s[8];

page_dir_entry *punt = (page_dir_entry*) 0x100000;
unsigned int mapa = 0x400000;

unsigned int dame_memoria(void) {

	unsigned int auxiliar = (unsigned int) punt; 
	punt = punt + 1024;
	return auxiliar;
}

unsigned int dame_mapa(void) {
	unsigned int res = mapa; 
	mapa = mapa + 0x1000;
	return res;
}

void mmu_inicializar_dir_kernel()
{

	page_dir_entry *pd = (page_dir_entry *) 0x27000;

	unsigned int i;
	for(i = 0;i < 1024; i++){

		pd[i].p   = 0;
        	pd[i].rw = 0;
        	pd[i].us = 0;
        	pd[i].pwt = 0;
        	pd[i].pcd = 0;
        	pd[i].a = 0;
        	pd[i].i = 0;
        	pd[i].ps = 0;
        	pd[i].g = 0;
        	pd[i].disponible = 0;
        	pd[i].dir_base = 0;
	}
 
	pd[0].p = 1;
	pd[0].rw = 1;
	pd[0].us = 0;
	pd[0].pwt = 0;
	pd[0].pcd = 0;
	pd[0].a = 0;
	pd[0].i = 0;
	pd[0].ps = 0;
	pd[0].g = 0;
	pd[0].disponible = 0;
	pd[0].dir_base = (unsigned int) 0x28;

	/*pd[1] = {1,1,0,0,0,0,0,0,0,0,0x29000}*/
	pd[1].p = 1;
	pd[1].rw = 1;
	pd[1].us = 0;
	pd[1].pwt = 0;
	pd[1].pcd = 0;
	pd[1].a = 0;
	pd[1].i = 0;
	pd[1].ps = 0;
	pd[1].g = 0;
	pd[1].disponible = 0; 
	pd[1].dir_base = (unsigned int) 0x29;

	/*pd[2] = {1,1,0,0,0,0,0,0,0,0,0x2A000};*/
	pd[2].p = 1; 
        pd[2].rw = 1; 
        pd[2].us = 0; 
        pd[2].pwt = 0; 
        pd[2].pcd = 0; 
        pd[2].a = 0; 
        pd[2].i = 0; 
        pd[2].ps = 0; 
        pd[2].g = 0; 
        pd[2].disponible = 0; 
        pd[2].dir_base = (unsigned int) 0x2A;	
	
	/*pd[3] = {1,1,0,0,0,0,0,0,0,0,0x2B000};*/
	pd[3].p = 1; 
	pd[3].rw = 1; 
	pd[3].us = 0; 
	pd[3].pwt = 0; 
	pd[3].pcd = 0; 
	pd[3].a = 0; 
	pd[3].i = 0; 
	pd[3].ps = 0; 
	pd[3].g = 0; 
	pd[3].disponible = 0; 
	pd[3].dir_base = (unsigned int) 0x2B;

	/*inicializar tabla level 2 */
	page_table_entry* bla = (page_table_entry*) 0x28000;
	for(i = 0; i < 1024; i++){
		bla[i].p = 1;
		bla[i].rw = 1;
		bla[i].us = 0;
		bla[i].pwt = 0;
		bla[i].pcd = 0;
		bla[i].a = 0 ;
		bla[i].d = 0;
		bla[i].pat = 0;
		bla[i].g = 0;
		bla[i].disponible = 0;
		bla[i].dir_base_p = i;	//4K*i
	}
	
	/*inicializar la tabla level 2*/
	bla = (page_table_entry*) 0x29000;
	for(i = 0; i< 1024; i++){
		bla[i].p = 1;
		bla[i].rw = 1;
		bla[i].us = 0;
		bla[i].pwt = 0;
		bla[i].pcd = 0;
		bla[i].a = 0;
		bla[i].d = 0;
		bla[i].pat = 0;
		bla[i].g = 0;
		bla[i].disponible = 0;
		bla[i].dir_base_p = (0x400 + i);	//4MB + 4K*i
	}
	
	/*inicializar la tabla level 2*/
	bla = (page_table_entry*) 0x2A000;
	for(i = 0; i <1024; i++){
		bla[i].p = 1;
		bla[i].rw = 1;
		bla[i].us = 0;
		bla[i].pwt = 0;
		bla[i].pcd = 0;
		bla[i].a = 0;
		bla[i].d = 0;
		bla[i].pat = 0;
		bla[i].g = 0;
		bla[i].disponible = 0;
		bla[i].dir_base_p = 0x800+i ; //8MB +4K*i
	}

	/*inicializar la tabla level 2 */
	bla = (page_table_entry*) 0x2B000;
	for(i = 0; i<452;i++){
		bla[i].p = 1;
		bla[i].rw = 1;
		bla[i].us = 0;
		bla[i].pwt = 0;
		bla[i].pcd = 0;
		bla[i].a = 0;
		bla[i].d = 0;
		bla[i].pat = 0;
		bla[i].g = 0;
		bla[i].disponible = 0;
		bla[i].dir_base_p = 0xC00 +i; //12MB + 4K*i
	}
}

void mmu_inicializar() {
	mmu_inicializar_dir_kernel();
}

unsigned int mmu_inicializar_dir_tarea(unsigned int tarea_id){

	unsigned int pdn = dame_memoria();
	page_dir_entry* pd1 = (page_dir_entry*) pdn; 
	unsigned int i;

        for(i = 0;i < 1024; i++){		//inicializo en cero todos los descriptores de tabla de páginas
                pd1[i].p = 0;
                pd1[i].rw = 1;
                pd1[i].us = 1;
                pd1[i].pwt = 0;
                pd1[i].pcd = 0;
                pd1[i].a = 0;
		        pd1[i].i = 0;
                pd1[i].ps = 0;
                pd1[i].g = 0;
                pd1[i].disponible = 0;
                pd1[i].dir_base = 0;
        }

	for(i=0;i<4;i++){			//completo 4 descriptores de tabla

		pd1[i].p = 1;
        	pd1[i].rw = 1;
        	pd1[i].us = 1;
        	pd1[i].pwt = 0;
        	pd1[i].pcd = 0;
        	pd1[i].a = 0;
        	pd1[i].i = 0;
        	pd1[i].ps = 0;
        	pd1[i].g = 0;
        	pd1[i].disponible = 0;
        	pd1[i].dir_base = (unsigned int) dame_memoria() >> 12;
	}


	unsigned j = 0;

	for(j=0;j<4;j++){

		page_table_entry* bl = (page_table_entry*) (pd1[j].dir_base << 12);

		for(i=0;i<1024;i++)

			bl[i].p=0;
	}



//mapeo los primeros 733MB

	for(j=0;j<3;j++){

		page_table_entry* bla = (page_table_entry*) (pd1[j].dir_base << 12);
        	for(i = 0; i < 1024; i++){
                	/*inicializar tabla level 2 */
                	bla[i].p = 1;
                	bla[i].rw = 1;
                	bla[i].us = 1;
                	bla[i].pwt = 0;
                	bla[i].pcd = 0;
                	bla[i].a = 0 ;
                	bla[i].d = 0;
                	bla[i].pat = 0;
                	bla[i].g = 0;
                	bla[i].disponible = 0;
                	bla[i].dir_base_p = i+0x400*j ; 
        	}
	}
	page_table_entry* bla = (page_table_entry*) (pd1[3].dir_base << 12);
	for(i = 0; i < 452; i++){
	/*inicializar tabla level 2 */
		bla[i].p = 1;
		bla[i].rw = 1;
		bla[i].us = 1;
		bla[i].pwt = 0;
		bla[i].pcd = 0;
		bla[i].a = 0 ;
		bla[i].d = 0;
		bla[i].pat = 0;
		bla[i].g = 0;
		bla[i].disponible = 0;
		bla[i].dir_base_p = i+0xC00; 
	}

	unsigned int mapa1 = dame_mapa();
	unsigned char* src = (unsigned char*)( (tarea_id*0x2000)+0x10000);
	unsigned char* dst = (unsigned char*) mapa1;

	for(i = 0; i < 0x2000;i++){
		dst[i] = src[i]; 
	}

	mmu_mapear_pagina(0x08000000, pdn,mapa1,0);
	mmu_mapear_pagina(0x08001000, pdn, dame_mapa(),0);


	return pdn;

}

void mmu_mapear_pagina(unsigned int virtual, unsigned int cr3, unsigned int fisica, int attrs){

	unsigned int aux = virtual;
	int i;
//	int estaMapeada = 0;
		
	aux = aux >> 22; 
	page_dir_entry *pd4 = (page_dir_entry*) (cr3 & 0xFFFFF000);
	page_table_entry* pt4;

	if(!(pd4[aux].p)){
	
		pd4[aux].p = 1;	
		pd4[aux].us = 1;
		pd4[aux].rw = 1;
		pd4[aux].dir_base = dame_memoria() >> 12;
	 	pt4 = (page_table_entry *) (pd4[aux].dir_base << 12);

		for(i=0;i<1024;i++)
			pt4[i].p = 0;

	}

//	breakpoint();
//Para que no mapee páginas ya mapeadas
/*	for(i = 0; i < 3; i++){
		if(pt4[i].p == 1 && pt4[i].dir_base_p == fisica) estaMapeada = 1;
	}
	if(estaMapeada == 0){
*/		pt4 = (page_table_entry *) (pd4[aux].dir_base << 12);
	
		aux = virtual << 10;
		aux = aux >> 22;

	//	if(!pt4[aux].p){

			pt4[aux].p = 1;
			pt4[aux].us = 1;
			pt4[aux].rw = 1;
			pt4[aux].dir_base_p = fisica >> 12;
	//	}	

	//		tlbflush();
//	}
}

void mmu_unmapear_pagina(unsigned int virtual, unsigned int cr3){

	page_dir_entry *pd2 = (page_dir_entry*) (cr3 & 0xFFFFF000);
	unsigned int aux = virtual >> 22;
	page_table_entry* pt2 = (page_table_entry *) (pd2[aux].dir_base << 12);
	aux = virtual << 10;
        aux = aux >> 22;
        pt2[aux].p = 0;

   tlbflush();     
}

void inicializar_tareas(void){

        unsigned int i = 0;

        for(i = 0; i < 8; i++){

                cr3s[i] = mmu_inicializar_dir_tarea(i);

        }

}

